package com.webanovare.ruzzle.solver;

import com.webanovare.ruzzle.misc.Trie;
import java.awt.Point;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Uses an algorithm to iterate through the board and find all possible words.
 */
public class Engine {

    private final Node ROOT;
    private List<String> words = new ArrayList<String>();
    private Character[][] board;
    private Trie trie;

    public Engine(Character[][] board, Trie trie) throws FileNotFoundException {
        this.board = board;
        this.trie = trie;

        ROOT = null;

        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board.length; j++) {
                Node startNode = new Node(ROOT, new ArrayList<Node>(), board[i][j] + "", j, i);
                findWords(startNode);
            }
        }
    }

    public boolean visited(Node n, Point p) {
        if (n.parent == null) {
            return false;
        }
        if (!p.equals(new Point(n.parent.x, n.parent.y))) {
            return visited(n.parent, p);
        }
        return true;
    }

    public Map<Point, String> getConnectedStrings(Node node) {
        Map<Point, String> newValues = new HashMap<Point, String>();
        for (int i = node.y - 1; i < node.y + 2; i++) {
            for (int j = node.x - 1; j < node.x + 2; j++) {
                if (!(i == node.y && j == node.x)) {
                    if (i >= 0 && i < board.length && j >= 0 && j < board.length) {
                        Point p = new Point(j, i);
                        if (!visited(node, p)) {
                            newValues.put(p, node.wordSoFar + board[i][j]);
                        }
                    }
                }
            }
        }
        return newValues;
    }

    private void findWords(Node current) {

        Map<Point, String> strings = getConnectedStrings(current);

        for (Point p : strings.keySet()) {
            String str = strings.get(p);
            if (!words.contains(str)) {
                if (trie.checkWord(str)) {
                    words.add(str);
                }
            }
            if (trie.hasExtension(str)) {
                current.children.add(new Node(current, new ArrayList<Node>(), str, p.x, p.y));
            }
        }
        if (!current.children.isEmpty()) {
            if (current.children != null) {
                for (Node n : current.children) {
                    findWords(n);
                }
            }
        }
    }

    public List<String> getWords() {
        Collections.sort(words, new StringComparable());
        return words;
    }

    class Node {

        private Node parent;
        private List<Node> children;
        private String wordSoFar;
        private int x, y;

        public Node(Node parent, List<Node> children, String wordSoFar, int x, int y) {
            this.parent = parent;
            this.children = children;
            this.wordSoFar = wordSoFar;
            this.x = x;
            this.y = y;
        }
    }
    
    class StringComparable implements Comparator<String>{
		 
	    @Override
	    public int compare(String s1, String s2) {
	        return (s1.length()<s2.length() ? -1 : (s1.length()==s2.length() ? 0 : 1));
	    }
    }
}
